package br.com.cocodonto.framework.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class DaoHelper {

	private static final ThreadLocal<Connection> context = new ThreadLocal<Connection>();

	/*
	 * Fornecer ao banco de dados
	 * 
	 * @return Connection
	 */
	public Connection getConnection() throws SQLException {
		Connection conn = null;

		try {
			Class.forName("org.apache.derby.jdbc.ClientDriver");
			conn = DriverManager.getConnection(
					"jdbc:derby://localhost:1527/coc", "app", "app");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return conn;
	}

	/* COME�O COD TRANSACTION */

	public void beginTransaction() throws SQLException {
		Connection conn = getConnection();
		conn.setAutoCommit(false);
		context.set(conn);
	}

	public void endTransaction() throws SQLException {
		commit(getConnectionFromContext());
		releaseTransaction();
	}

	public void releaseTransaction() throws SQLException {
		Connection conn = getConnectionFromContext();
		release(conn);
		context.remove();
	}

	public void rollbackTransaction() {
		Connection conn;
		try {
			conn = getConnectionFromContext();
			rollback(conn);
			release(conn);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		context.remove();
	}

	public void rollback(Connection conn) throws SQLException {
		if (conn != null)
			conn.rollback();
	}

	public void commit(Connection conn) throws SQLException {
		conn.commit();
	}

	public Connection getConnectionFromContext() throws SQLException {

		Connection conn = context.get();
		if (conn == null)
			throw new SQLException("Transa��o invalida");
		if (conn.isClosed())
			throw new SQLException(
					"Transa��o invalida, Conexao esta fechada :Z");
		return conn;

	}

	public <T> void executeQuery(String query, QueryMapping<T> queryMapping)
			throws SQLException {
		executeQuery(getConnection(), query, queryMapping);
	}

	public <T> void executeQuery(Connection conn, String query,
			QueryMapping<T> queryMapping) throws SQLException {

		Statement stmt = null;
		ResultSet rset = null;
		try {
			stmt = conn.createStatement();
			rset = stmt.executeQuery(query);
			queryMapping.mapping(rset);

		} finally {
			release(rset);
			release(stmt);
		}

	}

	public <T> void executePreparedQuery(String query,
			QueryMapping<T> queryMapping, Object... params) throws SQLException {
		executePreparedQuery(getConnection(), query, queryMapping, params);
	}

	public <T> void executePreparedQuery(Connection conn, String query,
			QueryMapping<T> queryMapping, Object... params) throws SQLException {
		PreparedStatement pstmt = conn.prepareStatement(query);
		ResultSet rset = null;
		try {
			pstmt = conn.prepareStatement(query);
			rset = pstmt.executeQuery();
			populatePreparedStatement(pstmt, params);
			queryMapping.mapping(rset);

		} finally {
			release(rset);
			release(pstmt);
		}
	}

	/* COME�O COD TRANSACTION */

	public long executePreparedUpdateAndReturnGeneratedKeys(String query,
			Object... params) throws SQLException {
		return executePreparedUpdateAndReturnGeneratedKeys(
				getConnectionFromContext(), query, params);
	}

	/* REFACTOR QUERY SQL */
	public long executePreparedUpdateAndReturnGeneratedKeys(Connection conn,
			String query, Object... params) throws SQLException {
		PreparedStatement pstmt = null;
		ResultSet rset = null;
		long result = 0l;
		try {
			pstmt = conn.prepareStatement(query,
					PreparedStatement.RETURN_GENERATED_KEYS);
			populatePreparedStatement(pstmt, params);
			pstmt.executeUpdate();
			rset = pstmt.getGeneratedKeys();
			if (rset.next())
				result = rset.getLong(1);
		} finally {
			release(pstmt);
			release(rset);
		}
		return result;
	}

	private void populatePreparedStatement(PreparedStatement pstmt,
			Object... params) throws SQLException {
		int i = 0;
		for (Object param : params) {
			pstmt.setObject(++i, param);
		}
	}

	public void executePreparedUpdate(String query, Object... params)
			throws SQLException {
		executePreparedUpdate(getConnectionFromContext(), query, params);
	}

	public void executePreparedUpdate(Connection conn, String query,
			Object... params) throws SQLException {
		PreparedStatement pstmt = null;
		try {
			pstmt = conn.prepareStatement(query);
			populatePreparedStatement(pstmt, params);
			pstmt.executeUpdate();
		} finally {
			release(pstmt);
		}

	}

	/* REFACTOR QUERY SQL */

	public void release(Statement stmt) {
		if (stmt == null)
			return;
		try {
			stmt.close();
		} catch (SQLException e) {
		}
	}

	public void release(Connection conn) {
		if (conn == null)
			return;
		try {
			conn.close();
		} catch (SQLException e) {
		}
	}

	public void release(ResultSet rset) {
		if (rset == null)
			return;
		try {
			rset.close();
		} catch (SQLException e) {
		}
	}

	public void releaseAll(Connection conn, Statement stmt) {
		release(stmt);
		release(conn);
	}

	public void releaseAll(Connection conn, Statement stmt, ResultSet rset) {
		release(rset);
		releaseAll(conn, stmt);
	}

}
